package com.xingchi.tornado.mybatisplus.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.jcommander.internal.Lists;
import com.xingchi.tornado.model.BaseDO;
import com.xingchi.tornado.model.BaseDTO;
import com.xingchi.tornado.model.PageResult;
import com.xingchi.tornado.model.PaginationQuery;
import com.xingchi.tornado.utils.BeanUtils;
import com.xingchi.tornado.utils.GenericTypeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ValidationUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 基于mybatisplus的抽象service实现
 *
 * @author xingchi
 * @date 2024/3/2 19:25
 */
public abstract class AbstractBasePlusService<DAO extends BaseMapper<DO>, DTO extends BaseDTO, Q extends PaginationQuery, DO extends BaseDO> extends ServiceImpl<DAO, DO> implements BasePlusService<DTO, Q> {

    @Autowired
    protected DAO mapper;

    protected DAO getDAO() {
        return mapper;
    }

    public abstract Wrapper<DO> getWrapper(Q query);

    @Override
    public Long count(Q query){
        return getDAO().selectCount(getWrapper(query));
    }

    @Override
    public List<DTO> selectByQuery(Q query){
        final Wrapper<DO> wrapper = getWrapper(query);

        if (!query.isPagination()) {
            return fromDO2DTO(getDAO().selectList(wrapper));
        }
        return fromDO2DTO(getDAO().selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper).getRecords());
    }

    @Override
    public PageResult<DTO> selectPageByQuery(Q query) {

        final Wrapper<DO> wrapper = getWrapper(query);
        if (query.isPagination()) {
            IPage<DO> records = getDAO().selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper);
            return PageResult.fetchPage(records, this::fromDO2DTO);
        }

        List<DTO> ts = fromDO2DTO(getDAO().selectList(wrapper));
        return PageResult.fetchPage(new Page<>(query.getPageNum(), ts.size(), ts.size()));
    }

    @Override
    public DTO selectOne(Q query) {
        final Wrapper<DO> wrapper = getWrapper(query);
        Page<DO> result = getDAO().selectPage(new Page<>(1, 1, false), wrapper);
        return fromDO2DTO(result.getRecords().stream().findFirst().orElse(null));
    }

    @Override
    public DTO insert(DTO record) {
        // 验证参数
        check(BASE_OP.INSERT, record);
        DO basePO = fromDTO2DO(record);
        getDAO().insert(basePO);
        record.setId(basePO.getId());

        return record;
    }

    @Override
    public Integer deleteById(Long id) {
        if (id == null) {
            return 0;
        }
        return getDAO().deleteById(id);
    }

    @Override
    public Integer deleteByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return 0;
        }
        return getDAO().deleteBatchIds(ids);
    }

    @Override
    public Integer updateById(DTO record) {
        check(BASE_OP.UPDATE, record);
        return getDAO().updateById(fromDTO2DO(record));
    }

    @Override
    public DTO selectById(Long id) {
        return fromDO2DTO(getDAO().selectById(id));
    }

    @Override
    public boolean exists(Long id) {
        return this.selectById(id) != null;
    }

    @Override
    public boolean check(BASE_OP baseOp, DTO baseDTO) {
        return true;
    }

    @Override
    public DTO saveOrUpdate(DTO record) {
        if (isUpdate(record)) {
            updateById(record);
            return record;
        }
        return insert(record);
    }

    protected boolean isUpdate(DTO record) {
        return record != null && record.getId() != null;
    }

    public List<DTO> fromDO2DTO(List<DO> list) {

        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }

        List<DTO> result = new ArrayList<>();
        for (DO baseDO : list) {
            DTO dto = fromDO2DTO(baseDO);
            if (dto != null) {
                result.add(dto);
            }
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    public DTO fromDO2DTO(BaseDO baseDO) {
        return fromDO2DTO(baseDO, (DTO) GenericTypeUtils.createInstance(this.getClass(), BaseService.class, 0));
    }

    @SuppressWarnings("unchecked")
    public DO fromDTO2DO(DTO baseDTO) {
        if (baseDTO == null) {
            return null;
        }

        return (DO) BeanUtils.convert(baseDTO, GenericTypeUtils.getSuperClassGenericType(getDAO().getClass(), BaseMapper.class, 0));
    }

    public DTO fromDO2DTO(BaseDO baseDO, DTO baseDTO) {
        if (baseDO == null) {
            return null;
        }
        BeanUtils.convert(baseDO, baseDTO);
        return baseDTO;
    }

}
